"""JT/T 808——2019"""
try:
    import ubinascii as binascii
except:
    import binascii


class EncryptMode(object):
    NULL = 0b000
    RSA = 0b001


class JTT808Config(object):
    __serialno_iter__ = iter(range(0, 10000))

    @classmethod
    def getSerialNumber(cls):
        try:
            return next(cls.__serialno_iter__)
        except StopIteration:
            cls.__serialno_iter__ = iter(range(0, 10000))
            return next(cls.__serialno_iter__)

    @classmethod
    def getVersion(cls):
        return getattr(cls, "__version__")

    @classmethod
    def getPhoneNumber(cls):
        return getattr(cls, "__phonenum__")

    @classmethod
    def getEncryptMode(cls):
        return getattr(cls, "__encryptmode__")

    @classmethod
    def update(cls, **kwargs):
        for k, v in kwargs.items():
            setattr(cls, "__" + k + "__", v)


class Message(object):
    """标识位、消息头、消息体、校验码、标识位"""

    def __init__(self, msgid, body, encryptmode, phonenum, serialno, version=1, subpack=False, **options):
        self.msgid = msgid
        self.body = body
        self.encryptmode = encryptmode
        self.version = version
        self.phonenum = phonenum
        self.serialno = serialno
        self.subpack = subpack
        self.options = options

    def __str__(self):
        return self.toHex().decode().upper()

    @classmethod
    def checksum(cls, data):
        out = data[0]
        for index in range(1, len(data)):
            out ^= data[index]
        return out

    def toBytes(self):
        raw = b''
        raw += self.msgid.to_bytes(2, "big")
        raw += ((1 << 14) | (self.subpack << 13) | (self.encryptmode << 10) | len(self.body)).to_bytes(2, "big")
        raw += self.version.to_bytes(1, "big")
        raw += self.phonenum
        raw += self.serialno.to_bytes(2, "big")
        if self.subpack:
            raw += self.options["total"].to_bytes(2, "big")
            raw += self.options["number"].to_bytes(2, "big")
        raw += self.body
        raw += self.checksum(raw).to_bytes(1, "big")
        raw = raw.replace(b"\x7D", b"\x7D\x01").replace(b"\x7E", b"\x7D\x02")
        raw = b"\x7E" + raw + b"\x7E"
        return raw

    @classmethod
    def fromBytes(cls, raw):
        if not (raw.startswith(b"\x7E") and raw.endswith(b"\x7E")):
            raise ValueError("not startswith & endswith 0x7E")
        data = raw[1:-1].replace(b"\x7D\x01", b"\x7D").replace(b"\x7D\x02", b"\x7E")
        if data[-1] != cls.checksum(data[:-1]):
            raise ValueError("crc check error")
        msgid = int.from_bytes(data[:2], "big")
        bodyattrs = int.from_bytes(data[2:4], "big")
        version = data[4]
        phonenum = data[5:15]
        serialno = int.from_bytes(data[15:17], "big")
        subpack = bool(bodyattrs & (1 << 13))
        encryptmode = (bodyattrs >> 10) & 0b11
        options = {}
        if subpack:
            options["total"] = int.from_bytes(data[17:19], "big")
            options["index"] = int.from_bytes(data[19:21], "big")
            body = data[21:-1]
        else:
            body = data[17:-1]
        if len(body) != (bodyattrs & ((1 << 10) - 1)):
            raise ValueError("body length error")
        self = cls(msgid, body, encryptmode, phonenum, serialno, version=version, subpack=subpack, **options)
        return self

    def toHex(self):
        return binascii.hexlify(self.toBytes())

    @classmethod
    def fromHex(cls, hex_string):
        return cls.fromBytes(binascii.unhexlify(hex_string))


class JTT808Msg(Message):
    msgid = None

    def __init__(self, body=b"", subpack=False, **options):
        if self.msgid is None:
            raise ValueError("unknown msgid")
        super().__init__(
            self.msgid,
            body,
            JTT808Config.getEncryptMode(),
            JTT808Config.getPhoneNumber(),
            JTT808Config.getSerialNumber(),
            version=JTT808Config.getVersion(),
            subpack=subpack,
            **options
        )


class JTT808MultiMsg(object):
    MAX_BODY_SIZE_PER_MESSAGE = (1 << 10) - 1

    def __init__(self, msgid, body):
        self.msgid = msgid
        self.body = body

    def split(self):
        temp = [i for i in range(0, len(self.body), self.MAX_BODY_SIZE_PER_MESSAGE)]
        return (
            Message(
                self.msgid,
                self.body[index:index+self.MAX_BODY_SIZE_PER_MESSAGE],
                JTT808Config.getEncryptMode(),
                JTT808Config.getPhoneNumber(),
                JTT808Config.getSerialNumber(),
                JTT808Config.getVersion(),
                subpack=True,
                total=len(temp),
                number=number
            )
            for number, index in enumerate(temp, 1)
        )


# >>>>>>>>>>>>>>>>终端消息<<<<<<<<<<<<<<<<<<<<<


class HeartBeatMsg(JTT808Msg):
    """终端心跳"""
    msgid = 0x0002


class QueryTimeMsg(JTT808Msg):
    """查询服务器时间"""
    msgid = 0x0004


class RegisterMsg(JTT808Msg):
    """终端注册"""
    msgid = 0x0100


class LogoutMsg(JTT808Msg):
    """终端注销"""
    msgid = 0x0003


class AuthenticateMsg(JTT808Msg):
    """终端鉴权"""
    msgid = 0x0102


class ReportLocationMsg(JTT808Msg):
    """位置信息汇报"""
    msgid = 0x0200


class ReportWaybillMsg(JTT808Msg):
    """电子运单上报"""
    msgid = 0x0701


class BulkReportLocationMsg(JTT808Msg):
    """定位数据批量上报"""
    msgid = 0x0704


class ReportCANDataMsg(JTT808Msg):
    """CAN总线数据上传"""
    msgid = 0x0705


class ReportMediaInfoMsg(JTT808Msg):
    """多媒体事件信息上传"""
    msgid = 0x0800


class ReportMediaDataMsg(JTT808Msg):
    """多媒体数据上传"""
    msgid = 0x0801


class DataUploadMsg(JTT808Msg):
    """数据上行透传"""
    msgid = 0x0900


class ReportGzipDataMsg(JTT808Msg):
    """数据压缩上报(GZIP压缩)"""
    msgid = 0x0901


class TerminalRSAPublicKeyMsg(JTT808Msg):
    """终端RSA公钥上报"""
    msgid = 0x0A00


class TerminalRespMsg(JTT808Msg):
    """终端通用应答"""
    msgid = 0x0001


class QueryParamsRespMsg(TerminalRespMsg):
    """查询终端参数应答"""
    msgid = 0x0104


class QueryAttrsRespMsg(TerminalRespMsg):
    """查询终端属性应答"""
    msgid = 0x0107


class UpgradeRespMsg(TerminalRespMsg):
    """终端升级结果应答"""
    msgid = 0x0108


class QueryLocationRespMsg(TerminalRespMsg):
    """位置信息查询应答"""
    msgid = 0x0201


class VehicleControlRespMsg(TerminalRespMsg):
    """车辆控制应答"""
    msgid = 0x0500


class QueryAreaOrRouteRespMsg(TerminalRespMsg):
    """查询区域或线路数据应答"""
    msgid = 0x0608


class QueryDrivingRecordsRespMsg(TerminalRespMsg):
    """行驶记录数据采集应答"""
    msgid = 0x0700


class QueryDriverInfoRespMsg(TerminalRespMsg):
    """驾驶员信息采集应答"""
    msgid = 0x0702


class CaptureRespMsg(TerminalRespMsg):
    """摄像头立即拍摄应答"""
    msgid = 0x0805


class QueryMediaDataRespMsg(TerminalRespMsg):
    """存储多媒体数据检索应答"""
    msgid = 0x0802


# >>>>>>>>>>>>>>>>平台消息<<<<<<<<<<<<<<<<<<<<<


class SetTerminalParamsMsg(JTT808Msg):
    """设置终端参数"""
    msgid = 0x8103


class QueryParamsMsg(JTT808Msg):
    """查询终端参数"""
    msgid = 0x8104


class QueryParamMsg(JTT808Msg):
    """查询指定终端参数"""
    msgid = 0x8106


class TerminalControlMsg(JTT808Msg):
    """终端控制"""
    msgid = 0x8105


class QueryAttrsMsg(JTT808Msg):
    """查询终端属性"""
    msgid = 0x8107


class UpgradeMsg(JTT808Msg):
    """下发终端升级包"""
    msgid = 0x8108


class QueryLocationMsg(JTT808Msg):
    """位置信息查询"""
    msgid = 0x8201


class LocateTimelyMsg(JTT808Msg):
    """临时位置跟踪"""
    msgid = 0x8202


class ManualConfirmAlarmMsg(JTT808Msg):
    """人工确认报警"""
    msgid = 0x8203


class LinkDetectMsg(JTT808Msg):
    """链路检测"""
    msgid = 0x8204


class TextDownloadMsg(JTT808Msg):
    """文本信息下发"""
    msgid = 0x8300


class PhoneCallbackMsg(JTT808Msg):
    """电话回拨"""
    msgid = 0x8400


class SetPhoneBookMsg(JTT808Msg):
    """设置电话本"""
    msgid = 0x8401


class VehicleControlMsg(JTT808Msg):
    """车辆控制"""
    msgid = 0x8500


class SetCircleMsg(JTT808Msg):
    """设置圆形区域"""
    msgid = 0x8600


class DelCircleMsg(JTT808Msg):
    """删除圆形区域"""
    msgid = 0x8601


class SetRectangleMsg(JTT808Msg):
    """设置矩形区域"""
    msgid = 0x8602


class DelRectangleMsg(JTT808Msg):
    """删除矩形区域"""
    msgid = 0x8603


class SetPolygonMsg(JTT808Msg):
    """设置多边形区域"""
    msgid = 0x8604


class DelPolygonMsg(JTT808Msg):
    """删除多边形区域"""
    msgid = 0x8605


class SetRouteMsg(JTT808Msg):
    """设置路线"""
    msgid = 0x8606


class DelRouteMsg(JTT808Msg):
    """删除路线"""
    msgid = 0x8607


class QueryAreaOrRouteMsg(JTT808Msg):
    """查询区域或线路"""
    msgid = 0x8608


class QueryDrivingRecordsMsg(JTT808Msg):
    """查询行驶记录"""
    msgid = 0x8700


class DrivingRecordParamsDownloadMsg(JTT808Msg):
    """行驶记录参数下传"""
    msgid = 0x8701


class QueryDriverInfoMsg(JTT808Msg):
    """查询驾驶员信息"""
    msgid = 0x8702


class CaptureMsg(JTT808Msg):
    """摄像头立即拍摄命令"""
    msgid = 0x8801


class QueryMediaDataMsg(JTT808Msg):
    """存储多媒体数据检索"""
    msgid = 0x8802


class QueryReportMediaDataMsg(JTT808Msg):
    """存储多媒体数据上传命令"""
    msgid = 0x8803


class StartVoiceRecordMsg(JTT808Msg):
    """录音开始命令"""
    msgid = 0x8804


class DataDownloadMsg(JTT808Msg):
    """数据下行透传"""
    msgid = 0x8900


class ServerRSAPublicKeyMsg(JTT808Msg):
    """平台RSA公钥"""
    msgid = 0x0A00


class ServerRespMsg(JTT808Msg):
    """平台通用应答"""
    msgid = 0x8001


class QueryTimeRespMsg(JTT808Msg):
    """查询服务器时间应答"""
    msgid = 0x8004


class RegisterRespMsg(JTT808Msg):
    """终端注册应答"""
    msgid = 0x8100


class ReportMediaDataRespMsg(JTT808Msg):
    """多媒体数据上传应答"""
    msgid = 0x8800
